/**
 * 
 */
package genealogy.deprecated.datamodel.pedigree;

import genealogy.deprecated.datamodel.pedigree.comparator.ComparatorFactory;
import genealogy.deprecated.datamodel.pedigree.comparator.NameComparator;
import genealogy.deprecated.datamodel.pedigree.sort.ChronologicalEventComparator;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.dftproject.lineagelinkage.metric.PedigreeScore;
import org.dftproject.lineagelinkage.metric.PedigreeTarget;
import org.dftproject.lineagelinkage.utils.Pair;


/**
 * @author Steve
 * @invariant assert(!(m_pedigreeA == null && m_pedigreeB == null));
 */
@Deprecated
public class PedigreePair implements Serializable{

	private static final long serialVersionUID = -3841843052583562537L;

	private Individual m_pedigreeA;
	
	private Individual m_pedigreeB;
	
	private PedigreeScore m_score;
	
	private ChronologicalEventComparator m_eventComparator;
	
	private static NameComparator m_nameComparator = ComparatorFactory.getNameComparator();
	
	/**
	 * Constructor
	 * 
	 * @param pedigreeA
	 * @param pedigreeB
	 * @pre non null args
	 * @post the object is created
	 */
	public PedigreePair(Individual pedigreeA, Individual pedigreeB, PedigreeScore score) {
		super();
		assert(pedigreeA != null);
		assert(pedigreeB != null);
		
		m_pedigreeA = pedigreeA;
		m_pedigreeB = pedigreeB;
		m_score = score;
		m_eventComparator = new ChronologicalEventComparator();
	}
	
	/**
	 * Constructor
	 * 
	 * @param pedigreeA
	 * @param pedigreeB
	 * @pre non null args
	 * @post the object is created
	 */
	public PedigreePair(Individual pedigreeA, Individual pedigreeB) {
		super();

		
		m_pedigreeA = pedigreeA;
		m_pedigreeB = pedigreeB;
		m_score = null;
		m_eventComparator = new ChronologicalEventComparator();
	}

	/**
	 * Getter for the pedigreeA
	 * 
	 * @return the pedigreeA
	 * @pre none
	 * @post result is not null
	 */
	public Individual getPedigreeA() {
		return m_pedigreeA;
	}

	/**
	 * Getter for the pedigreeB
	 * 
	 * @return the pedigreeB
	 * @pre none
	 * @post result is not null
	 */
	public Individual getPedigreeB() {
		return m_pedigreeB;
	}

	/**
	 * Getter for the score
	 * 
	 * @return the score
	 * @pre none
	 * @post result is not null
	 */
	public PedigreeScore getScore() {
		return m_score;
	}

	/**
	 * Setter for the score
	 * 
	 * @param score the score to set
	 * @pre score != null
	 * @post the score is set on this instance
	 */
	public void setScore(PedigreeScore score) {
		m_score = score;
	}
	
	public Pair<Name,Name> getNames(){
		Name nameA = null;
		Name nameB = null;
		if(m_pedigreeA != null){
			nameA = m_pedigreeA.getName();
		}
		if(m_pedigreeB != null){
			nameB = m_pedigreeB.getName();
		}
		return new Pair<Name,Name> (nameA, nameB);
	}
	
	public Pair<Gender,Gender> getGender(){
		// FIXME: chack for nulls, then return. See above for example
		return new Pair<Gender,Gender>(m_pedigreeA.getGender(), m_pedigreeB.getGender());
	}
	
	public List<Pair<LifeEvent,LifeEvent>> getEvents(){
		assert(!(m_pedigreeA == null && m_pedigreeB == null));
		List<Pair<LifeEvent,LifeEvent>> events = new ArrayList<Pair<LifeEvent,LifeEvent>>();
		
		if(m_pedigreeA == null || m_pedigreeB == null){
			List<LifeEvent> tempEvents = new ArrayList<LifeEvent>();
			if(m_pedigreeA != null){
				tempEvents.addAll(m_pedigreeA.getEvents().values());
				Collections.sort(tempEvents, m_eventComparator);
				for(LifeEvent event : tempEvents){
					events.add(new Pair<LifeEvent,LifeEvent>(event, null));
				}
			}
			else{
				tempEvents.addAll(m_pedigreeB.getEvents().values());
				Collections.sort(tempEvents, m_eventComparator);
				for(LifeEvent event : tempEvents){
					events.add(new Pair<LifeEvent,LifeEvent>(null,event));
				}
			}	
		}
		else{	
			Map<String,LifeEvent> eventsA = m_pedigreeA.getEvents();
			Map<String,LifeEvent> eventsB = m_pedigreeB.getEvents();
			List<LifeEvent> tempEvents = new ArrayList<LifeEvent>();
			tempEvents.addAll(eventsA.values());
			tempEvents.addAll(eventsB.values());
			Collections.sort(tempEvents,  m_eventComparator);
			
			Set<String> sortedEventNames = new TreeSet<String>();
			for(LifeEvent event : tempEvents){
				sortedEventNames.add(event.getEventName());
			}
			
			for(String eventName : sortedEventNames){
				if(eventName.length() > 0){
					events.add(new Pair<LifeEvent,LifeEvent>(eventsA.get(eventName),eventsB.get(eventName)));
				}
			}
		}
		if(events.size() == 0){
			return null;
		}
		return events;
	}
	
	public boolean hasFather(){
		return (m_pedigreeA != null && m_pedigreeA.hasFather()) || (m_pedigreeB != null && m_pedigreeB.hasFather());
	}
	
	public PedigreePair getFather(){
		Individual fatherA = (m_pedigreeA == null? null : m_pedigreeA.getFather());
		Individual fatherB = (m_pedigreeB == null? null : m_pedigreeB.getFather());
		return new PedigreePair(fatherA, fatherB);
	}
	
	public boolean hasMother(){
		return (m_pedigreeA != null && m_pedigreeA.hasMother()) || (m_pedigreeB != null && m_pedigreeB.hasMother());
	}
	
	public PedigreePair getMother(){
		Individual motherA = (m_pedigreeA == null? null : m_pedigreeA.getMother());
		Individual motherB = (m_pedigreeB == null? null : m_pedigreeB.getMother());
		return new PedigreePair(motherA, motherB);
	}
	
	public boolean hasSpouse(){
		return (m_pedigreeA != null && m_pedigreeA.hasSpouse()) || (m_pedigreeB != null && m_pedigreeB.hasSpouse());
	}
	
	
	private List<PedigreePair> getNullPairs(List<Individual> a, List<Individual> b){
		assert(!(a == null && b == null));
		List<PedigreePair> pairs = new ArrayList<PedigreePair>();
		if(a == null){
			for(Individual individual : b){
				pairs.add(new PedigreePair(null, individual));
			}
		}
		else{
			for(Individual individual : a){
				pairs.add(new PedigreePair(individual, null));
			}
		}
		if(pairs.size() == 0){
			return null;
		}
		return pairs;
	}
	
	public List<PedigreePair> getSpouses(){
		assert(!(m_pedigreeA == null && m_pedigreeB == null));
		if(m_pedigreeA == null || m_pedigreeB == null){
			if(m_pedigreeA != null){
				return getNullPairs(m_pedigreeA.getSpouses(), null);
			}
			else{
				return getNullPairs(null, m_pedigreeB.getSpouses());
			}
		}
		
		
		List<PedigreePair> spouses = getBestPairs(m_pedigreeA.getSpouses(), m_pedigreeB.getSpouses());
		if(spouses.size() == 0){
			return null;
		}
		return spouses;	
	}
	
	public boolean hasSibling(){
		return (m_pedigreeA != null && m_pedigreeA.hasSibling()) || (m_pedigreeB != null && m_pedigreeB.hasSibling());
	}
	
	public List<PedigreePair> getSiblings(){
		assert(!(m_pedigreeA == null && m_pedigreeB == null));
		if(m_pedigreeA == null || m_pedigreeB == null){
			if(m_pedigreeA != null){
				return getNullPairs(m_pedigreeA.getSiblings(), null);
			}
			else{
				return getNullPairs(null, m_pedigreeB.getSiblings());
			}
		}
		List<PedigreePair> Siblings = getBestPairs(m_pedigreeA.getSiblings(), m_pedigreeB.getSiblings());
		if(Siblings.size() == 0){
			return null;
		}
		return Siblings;	
	}
	
	public boolean hasChild(){
		return (m_pedigreeA != null && m_pedigreeA.hasChild()) || (m_pedigreeB != null && m_pedigreeB.hasChild());
	}
	
	public List<PedigreePair> getChildren(){
		assert(!(m_pedigreeA == null && m_pedigreeB == null));
		if(m_pedigreeA == null || m_pedigreeB == null){
			if(m_pedigreeA != null){
				return getNullPairs(m_pedigreeA.getChildren(), null);
			}
			else{
				return getNullPairs(null, m_pedigreeB.getChildren());
			}
		}
		List<PedigreePair> Children = getBestPairs(m_pedigreeA.getChildren(), m_pedigreeB.getChildren());
		if(Children.size() == 0){
			return null;
		}
		return Children;	
	}
	
	/**
	 * @return
	 * @pre
	 * @post
	 */
	public boolean isMatch(){
		return m_score.getTarget().equals(PedigreeTarget.MATCH);
	}
	
	private List<PedigreePair> getBestPairs(List<Individual> listA, List<Individual> listB){
		List<PedigreePair> pairs = new ArrayList<PedigreePair>();
		for(Individual individual : listA){
			double bssf = 0;
			Individual bestB = null;
			for(Individual individualB : listB){
				double score = m_nameComparator.compareNames(individual.getName(), individualB.getName());
				if(score > m_nameComparator.getDefaultValue() && score > bssf){
					bssf = score;
					bestB = individualB;
				}	
			}
			pairs.add(new PedigreePair(individual, bestB));
			listB.remove(bestB);
		}	
		
		for(Individual individualB : listB){
			pairs.add(new PedigreePair(null, individualB));
		}
		return pairs;
	}
	
	public RelationshipType getRelation(){
		assert(!(m_pedigreeA == null && m_pedigreeB == null));
		if(m_pedigreeA == null){
			return m_pedigreeB.getRelationship();
		}
		return m_pedigreeA.getRelationship();		
	}
}
